package com.ruoyi.sms.core.client.impl.aliyun;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson2.JSON;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.QuerySmsTemplateRequest;
import com.aliyun.dysmsapi20170525.models.QuerySmsTemplateResponse;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.ruoyi.sms.config.property.SmsChannelProperties;
import com.ruoyi.sms.core.client.SmsCommonResult;
import com.ruoyi.sms.core.client.dto.SmsReceiveRespDTO;
import com.ruoyi.sms.core.client.dto.SmsSendRespDTO;
import com.ruoyi.sms.core.client.dto.SmsTemplateRespDTO;
import com.ruoyi.sms.core.client.impl.AbstractSmsClient;
import com.ruoyi.sms.core.enums.SmsTemplateAuditStatusEnum;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;


/**
 * 阿里短信客户端的实现类
 *
 * @author ldl
 */
@Slf4j
public class AliyunSmsClient extends AbstractSmsClient {

    /**
     * REGION, 使用杭州
     */
    private static final String ENDPOINT = "dysmsapi.aliyuncs.com";

    /**
     * 阿里云客户端
     */
    private Client client;

    public AliyunSmsClient(SmsChannelProperties properties) {
        super(properties, new AliyunSmsCodeMapping());
        Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
        Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
    }

    @Override
    @SneakyThrows
    protected void doInit() {
        Config config = new Config()
                // 必填，您的 AccessKey ID
                .setAccessKeyId(properties.getApiKey())
                // 必填，您的 AccessKey Secret
                .setAccessKeySecret(properties.getApiSecret());
        // Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
        config.endpoint = ENDPOINT;
        client = new Client(config);
    }

    @Override
    protected SmsCommonResult doSendSms(Long sendLogId, String mobile,
                                        String apiTemplateId, LinkedHashMap<String, String> templateParams) {
        // 构建参数
        SendSmsRequest request = new SendSmsRequest();
        request.setPhoneNumbers(mobile);
        request.setSignName(properties.getSignature());
        request.setTemplateCode(apiTemplateId);
        request.setTemplateParam(JSON.toJSONString(templateParams));
        request.setOutId(String.valueOf(sendLogId));
        // 执行请求
        try {
            // 执行发送. 由于阿里云 sms 短信没有统一的 Response，但是有统一的 code、message、requestId 属性，所以只好反射
            SendSmsResponse sendResult = client.sendSms(request);
            String code = sendResult.getBody().getCode();
            String message = sendResult.getBody().getMessage();
            String requestId = sendResult.getBody().getRequestId();
            // 解析结果
            SmsSendRespDTO data = new SmsSendRespDTO(sendResult.getBody().bizId);
            if (Objects.equals(code, "OK")) { // 请求成功的情况下
                data = new SmsSendRespDTO(sendResult.getBody().bizId);
            }
            // 拼接结果
            return SmsCommonResult.build(code, message, requestId, data, codeMapping);
        } catch (Exception ex) {
            log.error("阿里云短信发送失败失败", ex);
            return SmsCommonResult.error(ex);
        }
    }

    @Override
    protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
        List<SmsReceiveStatus> statuses = JSON.parseArray(text, SmsReceiveStatus.class);
        return statuses.stream().map(status -> {
            SmsReceiveRespDTO resp = new SmsReceiveRespDTO();
            resp.setSuccess(status.getSuccess());
            resp.setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg());
            resp.setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime());
            resp.setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId()));
            return resp;
        }).collect(Collectors.toList());
    }

    @Override
    protected SmsCommonResult doGetSmsTemplate(String apiTemplateId) {
        // 构建参数
        QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
        request.setTemplateCode(apiTemplateId);
        // 执行请求
        try {
            // 复制代码运行请自行打印 API 的返回值
            QuerySmsTemplateResponse smsResult = client.querySmsTemplateWithOptions(request, new RuntimeOptions());
            String code = smsResult.getBody().code;
            String message = smsResult.getBody().message;
            String requestId = smsResult.getBody().requestId;
            SmsTemplateRespDTO data = null;
            if (Objects.equals(code, "OK")) { // 请求成功的情况下
                data = new SmsTemplateRespDTO()
                        .setId(smsResult.getBody().templateCode)
                        .setContent(smsResult.getBody().templateContent)
                        .setAuditStatus(convertSmsTemplateAuditStatus(smsResult.getBody().templateStatus))
                        .setAuditReason(smsResult.getBody().reason);
            }
            // 拼接结果
            return SmsCommonResult.build(code, message, requestId, data, codeMapping);
        } catch (Exception ex) {
            log.error("阿里云短信获取模板信息失败", ex);
            return SmsCommonResult.error(ex);
        }
    }

    Integer convertSmsTemplateAuditStatus(Integer templateStatus) {
        switch (templateStatus) {
            case 0:
                return SmsTemplateAuditStatusEnum.CHECKING.getStatus();
            case 1:
                return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
            case 2:
                return SmsTemplateAuditStatusEnum.FAIL.getStatus();
            default:
                throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus));
        }
    }

    /**
     * 短信接收状态
     * <p>
     * 参见 https://help.aliyun.com/document_detail/101867.html 文档
     *
     * @author ldl
     */
    @Data
    public static class SmsReceiveStatus {

        /**
         * 手机号
         */
        @JsonProperty("phone_number")
        private String phoneNumber;
        /**
         * 发送时间
         */
        @JsonProperty("send_time")
        @JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN, timezone = "GMT+8")
        private LocalDateTime sendTime;
        /**
         * 状态报告时间
         */
        @JsonProperty("report_time")
        @JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN, timezone = "GMT+8")
        private LocalDateTime reportTime;
        /**
         * 是否接收成功
         */
        private Boolean success;
        /**
         * 状态报告说明
         */
        @JsonProperty("err_msg")
        private String errMsg;
        /**
         * 状态报告编码
         */
        @JsonProperty("err_code")
        private String errCode;
        /**
         * 发送序列号
         */
        @JsonProperty("biz_id")
        private String bizId;
        /**
         * 用户序列号
         * <p>
         * 这里我们传递的是 SysSmsLogDO 的日志编号
         */
        @JsonProperty("out_id")
        private String outId;
        /**
         * 短信长度，例如说 1、2、3
         * <p>
         * 140 字节算一条短信，短信长度超过 140 字节时会拆分成多条短信发送
         */
        @JsonProperty("sms_size")
        private Integer smsSize;

    }

}
